<?php

//<editor-fold defaultstate="collapsed" desc="License & Copyright">
/*
 * @license
 */

/**
 *
 * @author Jevgeni Geimanen (jevgeni.geimanen@gmail.com)
 */
//</editor-fold>
class sCore {

    const PACKAGE_GENERATORS = "generators";
    const PACKAGE_SITE = "site";
    const PACKAGE_FORMS = "forms";
    const PACKAGE_CONFIGS = "configs";

    private static $_autoload_common_paths = array(
        "php/web",
        "php/web/cms",
        "php/web/forms",
        "php/web/forms/fields",
        "php/web/forms/validators",
        "php/web/pages",
        "php/web/components",
        "php/classes",
        "plugins/smarty",
        "plugins/lessphp"
    );

    /**
     *
     * @param SCStruct $object 
     */
    public static function getObjectClassFileName($object) {
        return ($res = self::getClassFileName($object->getClassName())) == null ? $object->getRefClass()->getFileName() : $res;
    }

    /**
     *
     * @param SCStruct $object 
     */
    public static function getClassFileName($className) {
        if (isset(self::$_CachedClassFilenames[$className]))
            return self::$_CachedClassFilenames[$className];
    }

    /**
     *
     * @param ReflectionClass $ref 
     * @return string;
     */
    public static function getRefClassFileName($ref) {
        return ($res = self::getClassFileName($ref->getName())) != null ? $res : $ref->getFileName();
    }

    private static $_CachedClassFilenames = array();

    public static function using($package) {
        switch ($package) {
            case self::PACKAGE_GENERATORS:
                self::$_autoload_common_paths[] = "php/{$package}/";
                break;
            case self::PACKAGE_SITE:
                //self::LoadPackage($package, array("SCStruct",  "IJSStruct", "SCComponent","SCContainer", "Container", "SCSite", "Site"));
                break;
            case self::PACKAGE_FORMS:
                self::LoadPackage($package, array("SCPage", "Page", "SCCMSPage", "CMSPage", "SCCollection", "ComponentCollection", "SCComponentHelper", "SCForm", "Form", "SCFormFieldCollection", "FormFieldCollection", "SCComponentLabels", "ComponentLabels", "SCFormFieldValidator", "FormFieldValidator", "RequiredFormFieldValidator", "SCFormField", "FormField", "SCTextFormField", "TextFormField", "SCFormFieldValidatorCollection", "FormFieldValidatorCollection", "SCSelectFormField", "SelectFormField", "SCRadioFormField", "RadioFormField", "SCCheckboxFormField", "CheckboxFormField", "SCDateTimePickerFormField", "DateTimePickerFormField", "SCButtonFormField", "ButtonFormField"));
                break;
            case self::PACKAGE_CONFIGS:
                //self::LoadPackage($package, array());
                break;
        }
    }

    public static function LoadPackage($package, $classes) {
        $list_filename = SCORE_APP_VAR_PATH . "/" . __CLASS__ . __FUNCTION__ . $package . ".php";
        if (!file_exists($filename = SCORE_APP_VAR_PATH . "/" . __CLASS__ . __FUNCTION__ . $package . ".package.php") || IS_DEV) {
            $files = array();
            $contents = array("<?php\n");
            foreach ($classes as $class) {
                $content = file_get_contents($fName = self::getAutoLoadCache($class));
                $content = preg_replace("/(\<\?php|\?>)/", "", $content);
                $contents[] = $content;
                $files[$class] = $fName;
            }
            file_put_contents($list_filename, serialize($files));
            file_put_contents($filename, join("\n", $contents));
        }

        if (is_array($list = unserialize(file_get_contents($list_filename)))) {
            self::$_CachedClassFilenames = array_merge(self::$_CachedClassFilenames, $list);
        }
        require_once $filename;
    }

    private $_AutoloadCachePath;
    //<editor-fold defaultstate="collapsed" desc="Instance">
    /**
     *
     * @var sCore
     */
    private static $_Instance = null;

    /**
     *
     * @return sCore 
     */
    public static function getInstance() {
        return self::$_Instance;
    }

    //</editor-fold>

    private function __construct($project_path) {
        self::$_Instance = $this;
        define('IS_DEV', true);
        define('SCORE_PATH', realpath(__DIR__ . "/./") . "/");
        define('SCORE_APP_PATH', realpath($project_path . "/") . "/");
        define('SCORE_APP_LIB_PATH', realpath($project_path . "/") . "/lib/");
        define('SCORE_APP_VAR_PATH', SCORE_APP_PATH . "var/");
        $this->_AutoloadCachePath = SCORE_APP_VAR_PATH . "__autoload.php";
        spl_autoload_register(__CLASS__ . "::AutoLoader");
    }

    //<editor-fold defaultstate="collapsed" desc="Site">
    private static $_Site;

    /**
     *
     * @return Site
     */
    public static function getSite() {
        if (self::$_Site == null) {
            sCore::using(sCore::PACKAGE_SITE);
            self::$_Site = Site::getInstance();
        }
        return self::$_Site;
    }

    /**
     *
     * @param Site $site
     * @return Site 
     */
    public function setSite(&$site) {
        $this->_Site = $site;
        return $this;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="AutoloadCachedPaths">
    /**
     * @var array
     */
    private $_AutoloadCachedPaths = null;

    /**
     *
     * @return array
     */
    public function getAutoloadCachedPaths() {
        if ($this->_AutoloadCachedPaths == null) {
            $this->_AutoloadCachedPaths = array();
            if (file_exists($this->_AutoloadCachePath))
                $this->_AutoloadCachedPaths = unserialize(file_get_contents($this->_AutoloadCachePath));
        }
        return $this->_AutoloadCachedPaths;
    }

    /**
     *
     * @param string $className 
     * @return string
     */
    public function getAutoloadCachedPath($className) {
        return isset($this->_AutoloadCachedPaths[$className]) && ($path = $this->_AutoloadCachedPaths[$className]) !== false ? $path : null;
    }

    /**
     *
     * @param string $className 
     * @param string $path
     * @return sCore
     */
    public function setAutoloadCachedPath($className, $path) {
        $this->_AutoloadCachedPaths[$className] = $path;
        file_put_contents($this->_AutoloadCachePath, serialize($this->_AutoloadCachedPaths));
        return $this;
    }

    //</editor-fold>

    private static $_AutoLoadCache = null;

    private static function getAutoLoadCache($className) {
        if ($className == 'SCCacheHelper')
            return self::AutoLoader_GetName($className);
        if (self::$_AutoLoadCache === null) {
            if ((self::$_AutoLoadCache = SCCacheHelper::getInstance()->getObject(__FUNCTION__)) === false) {
                self::$_AutoLoadCache = array();
            }
        }
        if (!key_exists($className, self::$_AutoLoadCache)) {
            self::$_AutoLoadCache[$className] = self::AutoLoader_GetName($className);
            SCCacheHelper::getInstance()->setObject(__FUNCTION__, self::$_AutoLoadCache);
        }
        return self::$_AutoLoadCache[$className];
    }

    //<editor-fold defaultstate="collapsed" desc="AutoLoader">
    private static function AutoLoader($className) {
        if (($res = self::getAutoLoadCache($className)) !== null)
            include_once $res;
    }

    private static function AutoLoader_GetName($className) {

        $core = self::getInstance();
        if (($path = $core->getAutoloadCachedPath($className)) === false)
            return null;
        else if ($path !== null)
            return $path;
        else if (($path = $core->getClassPathByClassName($className)) !== null) {
            $core->setAutoloadCachedPath($className, $path);
            return $path;
        }
    }

    //</editor-fold>

    /**
     *
     * @param string $className
     * @return boolean
     */
    public static function ClassExists($className) {
        $core = self::getInstance();
        return ($path = $core->getAutoloadCachedPath($className)) !== false && $path !== null || $path = $core->getClassPathByClassName($className) !== null;
    }

    /**
     *
     * @TODO Come up with a better solution for $common_paths variable.
     * 
     * @param string $class
     * @return string
     */
    private function getClassPathByClassName($class) {
        $name = null;
        if ($class == 'SCCacheHelper' || ($res = SCCacheHelper::getInstance()->getObject($name = __FUNCTION__ . $class)) === false || IS_DEV) {
            $res = null;
            $preffixes = array(
                SCORE_APP_PATH . "lib/",
                SCORE_PATH
            );

            $matches = array();
            preg_match("/^(.*)_(.*)$/", $class, $matches);

            //<editor-fold defaultstate="collapsed" desc="If Labels">
            if (count($matches) == 3 && $matches[2] == 'Labels') {
                //@TODO replace "sc" with current language
                $res = file_exists($path = dirname($this->getClassPathByClassName($matches[1])) . "/labels/sc.php") ? $path : null;
            }
            //</editor-fold >
            if (!$res) {
                $paths = array();
                foreach ($preffixes as $preffix)
                    foreach (self::$_autoload_common_paths as $common_path) {
                        if (count($matches) == 3) {
                            $paths[] = $preffix . $common_path . "/{$matches[1]}/";
                        }
                        $paths[] = $preffix . $common_path . "/$class/";
                        $paths[] = $preffix . $common_path . "/";
                    }
                foreach ($paths as $path)
                    if (file_exists($fpath = "{$path}$class.php")) {
                        $res = $fpath;
                        break;
                    }
            }
            if ($name != null)
                SCCacheHelper::getInstance()->setObject($name, $res);
        }
        return $res;
    }

    /**
     *
     * @var MainConfig
     */
    private static $_Config = null;

    /**
     *
     * @return MainConfig
     */
    public static function getConfig() {
        if (self::$_Config === null) {
            if ((self::$_Config = self::readApplicationConfig('main'))==null) {
                throw new Exception("Main config not found");
            }
            
        }
        return self::$_Config;
    }

    /**
     *
     * @param string $name
     * @return Config
     */
    public static function readApplicationConfig($name) {
        if (file_exists($iniFilePath = SCORE_APP_LIB_PATH . "ini/{$name}.ini")) {
            if (class_exists($className = ucfirst($name) . "Config")) {
                return $className::fromIniFile($iniFilePath);
            }
        }
    }

    /**
     *
     * @var string $project_path
     * @return sCore 
     */
    public static function factory($project_path) {
        return new sCore($project_path);
    }

}